import unsafe
import libc

const NULL = 1
const BOOL = 2
const I8 = 3
const U8 = 4
const I16 = 5
const U16 = 6
const I32 = 7
const U32 = 8
const I64 = 9
const U64 = 10
const INT = 9 // trans i64 in compiler time
const UINT = 10 // trans u64 in compiler time

const F32 = 11
const F64 = 12
const FLOAT = 12 // trans f64 in compiler time

const STRING = 13
const VEC = 14
const ARR = 15
const MAP = 16
const SET = 17
const TUP = 18
const STRUCT = 19
const FN = 20
const CHAN = 21
const COROUTINE_T = 22

const PTR = 23
const RAWPTR = 24
const ANYPTR = 25

const UNION = 26
const INTERFACE = 27

const PTR_SIZE = 8

type field_t = struct{
    string name
    int hash
    int offset
}

type type_t = struct{
    int size
    int hash
    int kind
    u8 align
    [int] hashes
    [field_t] fields // type struct exclusive

    rawptr<rtype_t> r
}

type rtype_struct_t = struct{
    i64 name_offset
    i64 hash
    i64 offset
}

type rtype_t = struct{
    u64 ident_offset
    u64 size
    u64 stack_size

    i64 hash
    u64 last_ptr
    u32 kind // type_kind
    i64 malloc_gc_bits_offset
    u64 gc_bits
    u8 align
    u16 length
    i64 hashes_offset
}

#linkid rt_find_rtype
fn find_rtype(int hash):rawptr<rtype_t>

#linkid rt_find_data
fn find_data(int offset):anyptr

#linkid rt_find_strtable
fn find_strtable(int offset):libc.cstr

fn typeof_hash(i64 hash):ptr<type_t>! {
    if hash <= 0 {
        throw errorf('hash must be greater than 0')
    }

    var r = find_rtype(hash)

    var t = new type_t(
        size = r.stack_size as i64,
        hash = r.hash,
        kind = r.kind as int,
        align = r.align,
        hashes = vec_new<int>(0, 0),
        fields = vec_new<field_t>(field_t{}, 0),
        r = r,
    )

    if r.kind == STRUCT {
        t.fields = vec_new<field_t>(field_t{}, r.length as int)
        var temp_fields = unsafe.vec_new(find_data(r.hashes_offset) as rawptr<rtype_struct_t>, r.length as int)

        for i,field in temp_fields {
            t.fields[i] = field_t{
                name: find_strtable(field.name_offset).to_string(),
                hash: field.hash,
                offset: field.offset,
            }
        }
    } else {
        if r.length > 0 {
            t.hashes = vec_new<int>(0, r.length as int)

            var temp_hashes = unsafe.vec_new(find_data(r.hashes_offset) as rawptr<i64>, r.length as int)
            var len = t.hashes.copy(temp_hashes)
            assert(len == t.hashes.len())
        }
    }

    return t
}

fn typeof<T>(T v):ptr<type_t>! {
    var hash = @reflect_hash(T)
    return typeof_hash(hash)
}

// kind base to string
fn type_t.to_string():string {
    return match self.kind {
        NULL -> 'null'
        BOOL -> 'bool'
        I8 -> 'i8'
        U8 -> 'u8'
        I16 -> 'i16'
        U16 -> 'u16'
        I32 -> 'i32'
        U32 -> 'u32'
        I64 -> 'i64'
        U64 -> 'u64'
        F32 -> 'f32'
        F64 -> 'f64'
        STRING -> 'string'
        VEC -> 'vec'
        ARR -> 'arr'
        MAP -> 'map'
        SET -> 'set'
        TUP -> 'tup'
        STRUCT -> 'struct'
        FN -> 'fn'
        CHAN -> 'chan'
        COROUTINE_T -> 'coroutine_t'
        PTR -> 'ptr'
        RAWPTR -> 'rawptr'
        ANYPTR -> 'anyptr'
        UNION -> 'union'
        _ -> 'unknown'
    }
}





// ----------------------------------------------- value of ------------------------------------------------------------
type vec_t = struct{
    anyptr data
    i64 length
    i64 capacity
    i64 element_size
    i64 hash
}

type string_t = vec_t

type map_t = struct{
    anyptr hash_table
    anyptr key_data
    anyptr value_data
    i64 key_hash
    i64 value_hash
    i64 length
    i64 capacity
}

type set_t = struct{
    anyptr hash_table
    anyptr key_data
    i64 key_hash
    i64 length
    i64 capacity
}

type union_t = struct{
    anyptr value
    rawptr<rtype_t> rtype
}

type value_t = struct{
    ptr<type_t> t
    anyptr v
}

fn valueof<T>(T v):ptr<value_t>! {
    var t = typeof<T>(v)
    if t.kind != VEC && t.kind != MAP && t.kind != SET {
        throw errorf('not value support type')
    }

    var result = new value_t(
        t = t,
        v = v as anyptr, // TODO union cannot use as to anyptr
    )

    return result
}

fn value_t.len():int {
    if self.t.kind == VEC {
        var v = self.v as rawptr<vec_t>
        return v.length as int
    }


    if self.t.kind == MAP {
        var v = self.v as rawptr<map_t>
        return v.length as int
    }

    if self.t.kind == SET {
        var v = self.v as rawptr<set_t>
        return v.length as int
    }

    assert(false)
    return 0
}